#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/wait.h>
#include <unistd.h>

// 父进程读取，子进程写入
int main()
{
    // 第一步: 创建文件，打开读写端
    int fds[2];
    // int pipe(int pipefd[2]);
    // pipe参数是输出型参数，pipe函数内部会填充pipe文件的fd到fds数组里面，修改pipe外面的fds数组内容
    int n = pipe(fds);
    assert(n == 0);

    // pipefd[0]-->read   pipefd[1]-->write
    // 0-->嘴巴 读书       1-->钢笔 写字
    // std::cout<<"fds[0]:" << fds[0] << std::endl;
    // std::cout<<"fds[1]:" << fds[1] << std::endl;

    // 第二步: fork
    pid_t id = fork();
    assert(id >= 0);
    if (id == 0)//子进程
    {
        close(fds[0]);
        // 子进程的通信代码，进行写入
        const char *msg = "我是子进程，我正在给你发消息";
        int cnt = 0;
        while (true) // 子进程不断向管道文件写入
        {
            char buffer[1024]; // 只有子进程能看到!!!
            snprintf(buffer, sizeof buffer, "child-->parent say: %s[%d][%d]", msg, cnt++, getpid());
            write(fds[1], buffer, strlen(buffer)); // 只需要将有效字段写入到文件即可，这是系统调用接口，\0是语言级的规定。
            sleep(1000000);                              // 每隔1s写一条信息
            // std::cout << "count: " << cnt << std::endl; // 写端写满的时候，继续写会发生阻塞，等待读端进行读取
            // break;
        }
        close(fds[1]); // 子进程关闭自己的写端描述符fd
        std::cout << "子进程关闭自己的写端" << std::endl;
        // sleep(10000);
        exit(0); // 退出码设置为0
    }  
    // 父进程的通信代码，进行读取
    close(fds[1]);
    while (true)
    {
        // sleep(10000);
        sleep(2);
        char buffer[1024];
        // 系统调用只认纯二进制，语言会做各种各样的处理，但我们不关心底层细节，防御式编程，都给你预留出来一个空间放\0
        std::cout << "AAAAAAAAAAAAAAAA" << std::endl;
        // 如果管道中没有了数据，读端再读，默认会直接阻塞当前正在读取的进程！进程等待管道文件，PCB会被放在文件的等待队列中，有数据之后会重新投入运行队列
        ssize_t s = read(fds[0], buffer, sizeof buffer - 1); //-1是为了将读取到buffer的内容进行处理，预留一个位置放\0
        std::cout << "BBBBBBBBBBBBBBBB" << std::endl;
        if (s > 0)
        {
            buffer[s] = 0;
            // 写入的一方没有考虑\0，他也不需要考虑\0，他认为他发送的就是一个个正常的二进制码，读取的一方读到的就是二进制，但将其作为字符串处理，加了\0
            std::cout << "Parent Get Message# " << buffer << " | parent pid: " << getpid() << std::endl;
        }
        else if (s == 0)
        {
            // 写端不写了，读端读到管道文件的结尾
            std::cout << "read: " << s << std::endl;
            break;
        }
        // break;
        // 细节: 父进程可没有sleep，一直进行读取
    }
    close(fds[0]);
    std::cout << "父进程关闭读端" << std::endl;

    int status = 0;
    n = waitpid(id, &status, 0); // 设置为0阻塞式等待
    assert(n == id);

    std::cout << "pid->" << n << "终止信号:" << (status & 0x7f) << std::endl;

    return 0;
}
